﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Babel.SDK
{
    sealed class TypeSignatureEqualityComparer : IEqualityComparer<string>
    {
        #region Consts
        private static string NAME = "name";
        private static string OPT = "opt";
        private static string CLOSE = "cl";
        private static string ARG = "arg";
        #endregion

        #region Fields
        private static readonly Regex _reSign = new Regex(
            @"^(?<name>^[^<]*)" +
            @"   (?>" +
            @"   (?:(?'op'<)[^<>]*)+" +
            @"   (?:(?'cl-op'>)(?<opt>[^<>]*))+" +
            @" )*" +
            @" (?(op)(?!))$",
            RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace);

        private static Regex _reArgLs = new Regex(
            @"(?>" +
            @"(?<arg>(?<name>[^<,]+)" +
            @"(?<gen>" +
            @"(?:" +
            @"  (?:(?'open'<)[^<]*)+" +
            @"  (?:(?'close-open'>)[^<,]*)+" +
            @")*" +
            @"(?(open)(?!))" +
            @"))\s*,?\s*" +
            @")*",
            RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace);

        private bool _skipNamespaceCheck;
        
        #endregion

        #region Properties
        public bool SkipNamespaceCheck
        {
            get { return _skipNamespaceCheck; }
            set { _skipNamespaceCheck = value; }
        }
        
        #endregion

        #region IEqualityComparer<string> Members

        public bool Equals(string x, string y)
        {
            if (x.Equals(y, StringComparison.Ordinal))
                return true;

            Match m1 = _reSign.Match(x);
            if (!m1.Success) return false;

            Match m2 = _reSign.Match(y);
            if (!m2.Success) return false;

            // Compare type names
            string name1 = m1.Groups[NAME].Value;
            string name2 = m2.Groups[NAME].Value;

            if (IsGenericPar(name1) || IsGenericPar(name2))
                return true;

            if (!name1.Equals(name2, StringComparison.Ordinal))
            {
                // Relaxed comparison without namespace
                if (!_skipNamespaceCheck)
                    return false;

				char[] seps = new char[] { '.', '/' };
				int p1 = name1.LastIndexOfAny(seps);
                if (p1 != -1)
                    name1 = name1.Substring(p1 + 1);

				int p2 = name2.LastIndexOfAny(seps);
                if (p2 != -1)
                    name2 = name2.Substring(p2 + 1);

                if (!name1.Equals(name2, StringComparison.Ordinal))
                    return false;
            }
            // Compare type specifier []&*
            string opt1 = m1.Groups[OPT].Value;
            string opt2 = m2.Groups[OPT].Value;

            if (!opt1.Equals(opt2, StringComparison.Ordinal))
                return false;

            string arglist1 = m1.Groups[CLOSE].Value;
            string arglist2 = m2.Groups[CLOSE].Value;

            // Check number of arguments
            if (arglist1.Length == 0 || arglist2.Length == 0)
                return ((arglist1.Length == 0) && (arglist2.Length == 0));

            // Get argument list 
            string[] args1 = SplitArgumentList(arglist1);
            string[] args2 = SplitArgumentList(arglist2);

            if (args1.Length != args2.Length)
                return false;

            // Check arguments
            for (int i = 0; i < args1.Length; i++)
            {
                if (!Equals(args1[i].Trim(), args2[i].Trim()))
                {
                    return false;
                }
            }

            return true;
        }

        public int GetHashCode(string obj)
        {
            return obj.GetHashCode();
        }

        private static bool IsGenericPar(string arg)
        {
            return arg.StartsWith("!");
        }

        private string[] SplitArgumentList(string arglist)
        {
            List<string> args = new List<string>();
            Match m = _reArgLs.Match(arglist);
            if (m.Success)
            {
                foreach (Capture c in m.Groups[ARG].Captures)
                {
                    args.Add(c.Value);
                }
            }
            return args.ToArray();
        }
        #endregion
    }
}
